package ai.people.core.grpc.config;

import ai.people.netmon.framework.constant.NmSystemConstant;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import lombok.RequiredArgsConstructor;
import net.devh.boot.grpc.server.security.authentication.GrpcAuthenticationReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.StringUtils;

import javax.annotation.Nullable;

/**
 * redis,grpc验证
 *
 * @author yuanqinglong
 * @date 2022/6/6 11:30
 */
@RequiredArgsConstructor
public class RedisGrpcAuthenticationReader implements GrpcAuthenticationReader {

    private static final Logger logger = LoggerFactory.getLogger(RedisGrpcAuthenticationReader.class);

    private final TokenStore redisTokenStore;

    /**
     * Tries to read the {@link Authentication} information from the given call and metadata.
     *
     * <p>
     * <b>Note:</b> Implementations are free to throw an {@link AuthenticationException} if no credentials could be
     * found in the call. If an exception is thrown by an implementation then the authentication attempt should be
     * considered as failed and no subsequent {@link GrpcAuthenticationReader}s should be called.
     * </p>
     *
     * @param call    The call to get that send the request.
     * @param headers The metadata/headers as sent by the client.
     * @return The authentication object or null if no authentication is present.
     * @throws AuthenticationException If the authentication details are malformed or incomplete and thus the
     *                                 authentication attempt should be aborted.
     */
    @Nullable
    @Override
    public Authentication readAuthentication(ServerCall<?, ?> call, Metadata headers) throws AuthenticationException {
        Metadata.Key<String> tokenKey = Metadata.Key.of(NmSystemConstant.AUTH_COOKIE_TOKEN_ID, Metadata.ASCII_STRING_MARSHALLER);
        String token = headers.get(tokenKey);
        OAuth2Authentication oAuth2Authentication;
        if (StringUtils.isEmpty(token) || (oAuth2Authentication = redisTokenStore.readAuthentication(token)) == null) {
            logger.debug("No grpc auth header found");
            return null;
        }
        // 将登录用户信息存储到security上下文
        SecurityContextHolder.getContext().setAuthentication(oAuth2Authentication);
        return null;
    }
}
